package com.stars.easyms.logger.support.logback.handler;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.TurboFilterList;
import ch.qos.logback.core.spi.FilterReply;
import com.stars.easyms.logger.EasyMsLoggerHandler;
import com.stars.easyms.logger.support.logback.util.EasyMsLoggerReflectUtil;
import org.slf4j.Marker;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import java.util.*;

/**
 * <p>className: EasyMsLogbackLoggerHandler</p>
 * <p>description: EasyMs自定义日志处理类</p>
 *
 * @author guoguifang
 * @date 2019-08-19 11:49
 * @since 1.3.0
 */
public final class EasyMsLogbackLoggerHandler implements EasyMsLoggerHandler<EasyMsLoggingEvent> {

    private static final EasyMsLogbackLoggerHandler INSTANCE = new EasyMsLogbackLoggerHandler();

    private final EasyMsLoggingEventFactory easyMsLoggingEventFactory = EasyMsLoggingEventFactory.getInstance();

    private final Map<String, Level> levelMap = new HashMap<>(8);

    private final Set<String> convertedLoggerSet = new HashSet<>();

    @Nullable
    @Override
    public EasyMsLoggingEvent filterAndGetEvent(org.slf4j.Logger slf4jLogger, Marker marker, String levelStr, String msg, Object[] params, Throwable throwable) {
        Logger logger = (Logger) slf4jLogger;
        String loggerName = logger.getName();
        if (!convertedLoggerSet.contains(loggerName)) {
            synchronized (this) {
                if (!convertedLoggerSet.contains(loggerName)) {
                    EasyMsLoggerReflectUtil.convertLogger(logger);
                    convertedLoggerSet.add(loggerName);
                }
            }
        }

        Level level = forLevel(levelStr);
        FilterReply decision = getTurboFilterChainDecision(logger, marker, level, msg, params, throwable);
        if (decision == FilterReply.NEUTRAL) {
            if (logger.getEffectiveLevel().levelInt > level.levelInt) {
                return null;
            }
        } else if (decision == FilterReply.DENY) {
            return null;
        }
        return easyMsLoggingEventFactory.getEasyMsLoggingEvent(logger, marker, level, msg, throwable, params);
    }

    private FilterReply getTurboFilterChainDecision(Logger logger, Marker marker, Level level, String format, Object[] params, Throwable t) {
        TurboFilterList turboFilterList = logger.getLoggerContext().getTurboFilterList();
        if (turboFilterList.isEmpty()) {
            return FilterReply.NEUTRAL;
        }
        return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, params, t);
    }

    @Override
    public void append(@NonNull EasyMsLoggingEvent loggingEvent) {
        loggingEvent.getLogger().callAppenders(loggingEvent);
    }

    private Level forLevel(String levelStr) {
        Level level = levelMap.get(levelStr);
        return level != null ? level : Level.INFO;
    }

    public static EasyMsLogbackLoggerHandler getInstance() {
        return INSTANCE;
    }

    private EasyMsLogbackLoggerHandler() {
        this.levelMap.put("ALL", Level.ALL);
        this.levelMap.put("TRACE", Level.TRACE);
        this.levelMap.put("DEBUG", Level.DEBUG);
        this.levelMap.put("INFO", Level.INFO);
        this.levelMap.put("WARN", Level.WARN);
        this.levelMap.put("ERROR", Level.ERROR);
        this.levelMap.put("OFF", Level.OFF);
    }
}